package client;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import messages.LoginMsg;
import messages.TicketListMsg;
import client.gui.InitDialog;
import client.gui.MainFrame;
import client.gui.MainPanel;
import client.gui.panels.Login;
import client.gui.panels.NotificationPanel;
import controllers.ControllerIF;
import controllers.LoginController;
import controllers.MainPanelController;
import entities.Message;
import entities.MsgType;
import entities.ObjectType;
import entities.Ticket;
import entities.User;

/**
 * ClientGUI is the first class to start in the client package. It starts the
 * login menu as well as holds information about the user
 */
public class MainController implements ClientIF {

	/**
	 * The default port to connect on.
	 */
	final public static int DEFAULT_PORT = 5555;

	/**
	 * The instance of the handler that communicates with the server.
	 */
	private ClientHandler clientHandler;

	/**
	 * reference of the main panel's controller
	 */
	private MainPanelController mainPanelController;
	
	/**
	 * the instance of the frame controller
	 */
	private MainFrame mainFrame;

	/**
	 * the instance of the user
	 */
	private User myUser;

	/**
	 * reference for the committee chairman 
	 */
	private User committeeChairmam;
	
	/**
	 * reference for both committee members 
	 */
	private ArrayList<User> committeeMembers;
	
	
	
	public MainController(String host, int port) {
		try {
			clientHandler = new ClientHandler(host, port, this);
		} catch (IOException exception) {
			System.out.println("Error: Can't setup connection!"	+ " Terminating client.");
			System.exit(1);
		}
	}

	/**
	 * The program's starting point
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		String host = ""; // Hostname to connect to
		Integer port = 0; // The port number
		// Set System Look and Feel
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (UnsupportedLookAndFeelException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}

		InitDialog initFrame = new InitDialog();
		// while the JDialog is showing - wait for it to finish
		while(true) {
			try {
				if(initFrame.isShowing()) {
					Thread.sleep(1000);
				}
				else break;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		host = initFrame.getHost();
		port = initFrame.getPort();
		
		// host = "192.168.10.19";
		//host = "localhost";
		// port = 5555;
		// reference is null - Garbage Collector will free the memory
		initFrame = null;
		
		if (host.equals(null) || port == 0) {
			System.err.println("Error: hostname or port is missing.");
			System.exit(1);
		}
		else {
			// Create instance of the Main controller and Client handler
			MainController cs = new MainController(host, port);
			// Initiate the UI
			cs.initLoginUI();
			 
			// cs.loginRequest("38103958", "123456");
		}
		
	}

	/**
	 * Start the GUI with the login screen
	 */
	@Override
	public void initLoginUI() {
		mainFrame = new MainFrame(this);
		ControllerIF loginController = new LoginController(mainFrame, new Login());

		mainFrame.displayPanel(loginController.getPanel());
	}

	/**
	 * Get the connected user
	 */
	public User getUser() {
		return myUser;
	}
	
	/**
	 * Get the user's first name
	 * 
	 * @return String
	 */
	public String getUserFirstName() {
		return myUser.getFirstName();
	}
	
	/**
	 * Get the committee chairman
	 * @return User
	 */
	public User getCommitteeChairman() {
		return committeeChairmam;
	}
	
	/**
	 * Get a list of the committee members (chairman not included)
	 * @return List<User>
	 */
	public List<User> getCommitteeMembers() {
		return committeeMembers;
	}
	
	
	/**
	 * Transfers the login message to client handler
	 */
	public void loginRequest(String username, String password) {
		Message loginMsg = new Message(MsgType.LOGIN_REQUEST, ObjectType.LOGIN, new LoginMsg(username, password));
		clientHandler.handleMessageFromClientUI(loginMsg);
	}

	/**
	 * Transfer a logout message to client handler
	 */
	public void logoutRequest() {
		Message message = new Message(MsgType.LOGOUT_REQUET, ObjectType.LONG, myUser.getId());
		clientHandler.handleMessageFromClientUI(message);
	}
	
	/**
	 * request a list of the existing information systems
	 */
	public void informationSystemRequest() {
		Message message = new Message(MsgType.INFO_SYSTEMS_REQUEST, null, null);
		clientHandler.handleMessageFromClientUI(message);
	}
	
	/**
	 * Send a ticket to server 
	 * @param ticket
	 */
	public void storeTicket(Ticket ticket) {
		ticket.setUser(myUser);
		Message message = new Message(MsgType.NEW_TICKET, ObjectType.TICKET, ticket);
		clientHandler.handleMessageFromClientUI(message);
	}
	
	/**
	 * send ticket details request to server
	 * @param ticketId
	 */
	public void ticketDetailsByIdRequest(Long ticketId) {
		Message message = new Message(MsgType.TICKET_DETAILS_REQUEST, ObjectType.LONG, ticketId);
		clientHandler.handleMessageFromClientUI(message);
	}
	
	/**
	 * Sends a request for bunch of tickets according to a combination of user id and specific request
	 * @param msgType
	 * @param statusName represents ticket's status - new, assigning etc.
	 * @param userIndicator represents the user role in the system
	 */
	public void ticketsListRequest(MsgType msgType, String statusName, String userIndicator) {
		Message message;
		
		if( msgType.equals(MsgType.TICKET_LIST_ALL_REQUEST) || msgType.equals(MsgType.TICKET_LIST_STATUS_REQUEST) || msgType.equals(MsgType.TICKET_LIST_STAGE_REQUEST)){
			message = new Message(msgType, ObjectType.TICKET_LIST_MSG, new TicketListMsg(statusName, myUser.getId(), userIndicator));
		}
		else if (msgType.equals(MsgType.LIST_ALL_USERS_REQUEST)){
			message = new Message(msgType, null, null);
		}
		else if(msgType.equals(MsgType.LIST_TIME_EXTENSTIONS_REQUESTS_REQUEST)){
			ArrayList<String> queryParms = new ArrayList<String>();
			queryParms.add(myUser.getId().toString());
			message = new Message(msgType, null, queryParms);
		}
		else{
			message = new Message(null,null,null);
		}
		clientHandler.handleMessageFromClientUI(message);
	}
		
	/**
	 * Sends a request for a single ticket progress tracking
	 * @param ticketId Long
	 */
	public void ticketTrackingRequest(Long ticketId) {
		Message message = new Message(MsgType.TICKET_TRACK_REQUEST, ObjectType.LONG, ticketId);
		clientHandler.handleMessageFromClientUI(message);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public void readMsg(Message message) {

		/***************************************************
		 ***  Handle incoming committee members message  ***
		 ***************************************************/
		if(message.getMsgType().equals(MsgType.LIST_ALL_COMMITTEE_MEMBERS_ERR) || message.getMsgType().equals(MsgType.LIST_ALL_COMMITTEE_MEMBERS_EMPTY)) {
			// do nothing
		}
		else if(message.getMsgType().equals(MsgType.LIST_ALL_COMMITTEE_MEMBERS_RESPONSE)) {
			committeeMembers = (ArrayList<User>) message.getObject();
			
			committeeChairmam = committeeMembers.get(0);
			committeeMembers.remove(0);
		}		
		
		/**************************************************
		 **********  Handle login type messages  **********
		 **************************************************/
		else if (message.getMsgType().equals(MsgType.LOGIN_CONNECTED_ERR)) {
			mainFrame.showErrorMessage("User is already connected.");
		} else if (message.getMsgType().equals(MsgType.LOGIN_CONNETION_ERR)) {
			mainFrame.showErrorMessage("Error: Connection with server dropped.");
		} else if (message.getMsgType().equals(MsgType.LOGIN_USER_PSW_ERR)) {
			mainFrame.showErrorMessage("Incorrect username or password.");
		} else if (message.getMsgType().equals(MsgType.LOGIN_OK)) {
			if (message.getObject().getClass() == null) {
				mainFrame.showErrorMessage("Failed to connect to server.");
			} else {
				// Save the user's instance
				myUser = (User) message.getObject();

				/// replace login panel with the main panel
				mainFrame.resetBounds(10, 10, 940, 700);
				mainPanelController = new MainPanelController(this);
				MainPanel tmpPanel = new MainPanel(mainPanelController, mainFrame);
				mainFrame.displayPanel(tmpPanel);
				sendMessage(new Message(MsgType.LIST_ALL_COMMITTEE_MEMBERS_REQUEST, null, null));
			}
		}
		
		/**************************************************
		 *****  Handle information systems messages  ******
		 **************************************************/
		else if(message.getMsgType().equals(MsgType.INFO_SYSTEMS_RESPONSE_ERR)) {
			mainFrame.showErrorMessage("Error: Failed to retrive data from server");
		}
		else if(message.getMsgType().equals(MsgType.INFO_SYSTEMS_RESPONSE)) {
			if(message.getObject().getClass() == null) {
				mainFrame.showErrorMessage("Error: Failed to retrive data from server");
			}
			else {
				mainPanelController.decipherMessage(message);
			}
		}

		/**************************************************
		 ******  Handle new ticket storage response  ******
		 **************************************************/
		else if(message.getMsgType().equals(MsgType.TICKET_ERR)) {
			mainFrame.showErrorMessage("Error: Server writing error.");			
		}
		else if(message.getMsgType().equals(MsgType.TICKET_RESPONSE)) {
			mainFrame.showSplashMessage("New ticket ID: " + message.getObject().toString());
			String msg = "Your ticket has saved successfully. \nThe ticket ID for progress tracking is: " + message.getObject().toString();
			NotificationPanel notifiPanel = new NotificationPanel(msg);
			mainPanelController.setInnerPanel(notifiPanel);
		}
		
		/**************************************************
		 ****  Handle ticket tracking server response  ****
		 **************************************************/
		else if(message.getMsgType().equals(MsgType.TICKET_TRACK_ERR)) {
			mainFrame.showErrorMessage("Error: Can't fench ticket details");
		}
		else if(message.getMsgType().equals(MsgType.TICKET_TRACK_RESPONSE)) {
			mainPanelController.decipherMessage(message);
		}
		
		/**************************************************
		 *****  Handle ticket details server response  ****
		 **************************************************/
		else if(message.getMsgType().equals(MsgType.TICKET_DETAILS_ERR)) {
			mainFrame.showErrorMessage("Error: Can't fetch ticket details.");			
		}
		else if(message.getMsgType().equals(MsgType.TICKET_DETAILS_RESPONSE)) {
			mainPanelController.decipherMessage(message);
		}
		
		/**************************************************
		 *******  Handle lists server response  *****
		 **************************************************/
		// Any type of response will be handled in the appropriate controller
		else if((message.getMsgType().equals(MsgType.TICKET_LIST_ERR)) || (message.getMsgType().equals(MsgType.TICKET_LIST_EMPTY)) || (message.getMsgType().equals(MsgType.TICKET_LIST_RESPONSE))) {
			mainPanelController.decipherMessage(message);
		}		
		else if((message.getMsgType().equals(MsgType.LIST_ALL_USERS_ERR)) || (message.getMsgType().equals(MsgType.LIST_ALL_USERS_EMPTY)) || (message.getMsgType().equals(MsgType.LIST_ALL_USERS_RESPONSE))) {
			mainPanelController.decipherMessage(message);
		}
		else if((message.getMsgType().equals(MsgType.LIST_TIME_EXTENSTIONS_REQUESTS_ERR)) || (message.getMsgType().equals(MsgType.LIST_TIME_EXTENSTIONS_REQUESTS_EMPTY)) || (message.getMsgType().equals(MsgType.LIST_TIME_EXTENSTIONS_REQUESTS_RESPONSE))) {
			mainPanelController.decipherMessage(message);
		}		
		else if((message.getMsgType().equals(MsgType.APPROVE_TIME_EXTENSION_REQUEST_RESPONSE))) {
			mainFrame.showSplashMessage("Time Extension Decision has been upated.");
		}
		
		/**************************************************
		 *******    ******
		 **************************************************/

	}

	/**
	 * Sends string to frame to show as error 
	 * @param string
	 */
	public void showErrorMessage(String string) {
		mainFrame.showErrorMessage(string);
	}

	/**
	 * Sends string to frame to show as regular message
	 * @param string
	 */
	public void showSplashMessage(String string) {
		mainFrame.showSplashMessage(string);
	}


	/**
	 * Sends a given message to server
	 * @param message Message
	 */
	public void sendMessage(Message message) {
		clientHandler.handleMessageFromClientUI(message);
	}

}
